/**
*    CTR - Centro Tecnologia Rocci
*    Open Source Java Classic Game Toolkit
*
*    (C) 2010, CTR - Centro Tecnologia Rocci
*
*    This library is free software; you can redistribute it and/or
*    modify it under the terms of the GNU Lesser General Public
*    License as published by the Free Software Foundation;
*    version 2.1 of the License.
*
*/
package it.ctr.game.toolkit;

import it.ctr.game.toolkit.component.Mario;
import it.ctr.game.toolkit.stage.LoseStage;
import it.ctr.game.toolkit.stage.MapStage;
import it.ctr.game.toolkit.stage.Stage;
import it.ctr.game.toolkit.stage.TitleStage;
import it.ctr.game.toolkit.stage.WinStage;
import it.ctr.game.toolkit.stage.level.LevelStage;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.VolatileImage;
import java.util.Random;

import javax.swing.JComponent;
import javax.swing.text.html.HTMLDocument.HTMLReader.IsindexAction;

import org.omg.CosNaming.IstringHelper;


/**
 * @author Luca Rocci, 09/ago/2010
 * @
 */
public class GameComponent extends JComponent implements Runnable, KeyListener, FocusListener {

	private static final long serialVersionUID = 2728448906615689001L;
	public static final int FRAME = 24;
	public static final int WIDTH = 800;
	public static final int HEIGHT = 572;

    private boolean focused = false;

	private boolean running = false;
	private GraphicsConfiguration graphicsConfiguration;
	private Stage scene;

	private MapStage mapScene;

	/**
	 * @param width
	 * @param height
	 */
	public GameComponent() {
		setFocusable(true);
		setEnabled(true);
		Dimension size = new Dimension(WIDTH, HEIGHT);
        setPreferredSize(size);
        setMinimumSize(size);
        setMaximumSize(size);
		setFocusable(true);
	}
		
	   private void toggleKey(int keyCode, boolean isPressed)
	    {
	        if (keyCode == KeyEvent.VK_LEFT)
	        {
	            scene.setKey(Mario.KEY_LEFT, isPressed);
	        }
	        if (keyCode == KeyEvent.VK_RIGHT)
	        {
	            scene.setKey(Mario.KEY_RIGHT, isPressed);
	        }
	        if (keyCode == KeyEvent.VK_DOWN)
	        {
	            scene.setKey(Mario.KEY_DOWN, isPressed);
	        }
	        if (keyCode == KeyEvent.VK_UP)
	        {
	            scene.setKey(Mario.KEY_UP, isPressed);
	        }
	        if (keyCode == KeyEvent.VK_A)
	        {
	            scene.setKey(Mario.KEY_SPEED, isPressed);
	        }
	        if (keyCode == KeyEvent.VK_S)
	        {
	            scene.setKey(Mario.KEY_JUMP, isPressed);
	        }

	    }

	    public void paint(Graphics g)
	    {
	    }

	    public void update(Graphics g)
	    {
	    }

	    public void start()
	    {
	        if (!running)
	        {
	            running = true;
	            new Thread(this, "CTR.Game.Toolkit").start();
	        }
	    }

	    public void stop()
	    {
	    	BrickBox.stopMusic();
	        running = false;
	    }

	    public void run()
	    {
	        graphicsConfiguration = getGraphicsConfiguration();

	        mapScene = new MapStage(graphicsConfiguration, this, new Random().nextLong());
	        scene = mapScene;

	        BrickBox.init(graphicsConfiguration);

	        VolatileImage image = createVolatileImage(WIDTH, HEIGHT);
	        Graphics g = getGraphics();
	        Graphics og = image.getGraphics();

	        int lastTick = -1;

	        int renderedFrames = 0;

	        double time = System.nanoTime() / 1000000000.0;
	        double now = time;
	        double averagePassedTime = 0;

	        addKeyListener(this);

	        boolean naiveTiming = true;

	        toTitle();

	        while (running)
	        {
	            double lastTime = time;
	            time = System.nanoTime() / 1000000000.0;
	            double passedTime = time - lastTime;

	            if (passedTime < 0) naiveTiming = false; // Stop relying on nanotime if it starts skipping around in time (ie running backwards at least once). This sometimes happens on dual core amds.
	            averagePassedTime = averagePassedTime * 0.9 + passedTime * 0.1;

	            if (naiveTiming)
	            {
	                now = time;
	            }
	            else
	            {
	                now += averagePassedTime;
	            }

	            int tick = (int) (now * FRAME);
	            if (lastTick == -1) lastTick = tick;
	            while (lastTick < tick)
	            {
	                scene.tick();
	                lastTick++;

	                if (lastTick % FRAME == 0)
	                {
	                    renderedFrames = 0;
	                }
	            }

	            float alpha = (float) (now * FRAME - tick);

	            scene.render(og, alpha);

	            og.setColor(Color.BLACK);
	            g.drawImage(image, 0, 0, null);

	            renderedFrames++;

	            try
	            {
	                Thread.sleep(5);
	            }
	            catch (InterruptedException e)
	            {
	            	 BrickBox.stopMusic();
	            }
	        }

	        BrickBox.stopMusic();
	    }

	    private void drawString(Graphics g, String text, int x, int y, int c)
	    {
	        char[] ch = text.toCharArray();
	        for (int i = 0; i < ch.length; i++)
	        {
	            g.drawImage(BrickBox.font[ch[i] - 32][c], x + i * 8, y, null);
	        }
	    }

	    public void keyPressed(KeyEvent arg0)
	    {
	        toggleKey(arg0.getKeyCode(), true);
	    }

	    public void keyReleased(KeyEvent arg0)
	    {
	        toggleKey(arg0.getKeyCode(), false);
	    }

	    public void startLevel(long seed, int difficulty, int type)
	    {
	        scene = new LevelStage(graphicsConfiguration, this, seed, difficulty, type);
	        scene.init();
	    }

	    public void levelFailed()
	    {
	        scene = mapScene;
	        mapScene.startMusic();
	        Mario.lives--;
	        if (Mario.lives == 0)
	        {
	            lose();
	        }
	    }

	    public void keyTyped(KeyEvent arg0)
	    {
	    }

	    public void levelWon()
	    {
	        scene = mapScene;
	        mapScene.startMusic();
	        mapScene.levelWon();
	    }
	    
	    public void win()
	    {
	        scene = new WinStage(this);
	        scene.init();
	    }
	    
	    public void toTitle()
	    {
	        Mario.resetStatic();
	        scene = new TitleStage(this, graphicsConfiguration);
	        scene.init();
	    }
	    
	    public void lose()
	    {
	        scene = new LoseStage(this);
	        scene.init();
	    }

	    public void startGame()
	    {
	        scene = mapScene;
	        mapScene.startMusic();
	        mapScene.init();
	   }

	    public void focusGained(FocusEvent arg0)
	    {
	        focused = true;
	    }

	    public void focusLost(FocusEvent arg0)
	    {
	        focused = false;
	    }
	    
}
